#pragma once

#include <iostream>
#include <string>
#include <queue>
#include "Task.hpp"

const int defaultcap = 5;

template <class T>
class BlockQueue
{
    bool IsFull() { return _q.size() >= _cap; }
    bool IsEmpty() { return _q.empty(); }

public:
    BlockQueue(int cap = defaultcap)
        : _cap(cap),
          _csleep_num(0),
          _psleep_num(0)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_full_cond, nullptr);
        pthread_cond_init(&_empty_cond, nullptr);
    }

    void Equeue(const T &in)
    {
        // 队列是临界资源
        pthread_mutex_lock(&_mutex);
        while (IsFull())
        {
            // 满了就应该让生产者进行等待
            // 1.pthread_cond_wait调用成功，挂起当前的线程之前，要先把锁自动释放（所以满等待的时候，不影响消费的过程）
            //   这也是需要传入锁的原因
            // 2.当线程被唤醒的时候，默认在临界区内唤醒，从pthread_cond_wait成功返回（是在临界区被唤醒的）
            //   ，需要当前的进程重新申请mutex锁
            // 3.被唤醒之后，没抢到锁？？会在锁上组阻塞等待
            _psleep_num++;
            //pthread_cond_wait失败了怎么搞？？会立即返回
            //可能会被伪唤醒
            std::cout<<"生产者进入休眠"<<std::endl;
            pthread_cond_wait(&_full_cond, &_mutex);
            _psleep_num--;
        }
        _q.push(in);
        //到这里队列里一定会有数据，如果消费者在休眠就唤醒它
        if(_csleep_num>0)
        {
            pthread_cond_signal(&_empty_cond);
            std::cout<<"唤醒消费者"<<std::endl;
        }
        pthread_mutex_unlock(&_mutex);
    }
    T Pop()
    {
        pthread_mutex_lock(&_mutex);
        while (IsEmpty())
        {
            _csleep_num++;
            pthread_cond_wait(&_empty_cond, &_mutex);
            _csleep_num--;
        }
        T data = _q.front();
        _q.pop();

        if(_psleep_num>0)
        {
            pthread_cond_signal(&_full_cond);
            std::cout<<"唤醒生产者"<<std::endl;
        }
        pthread_mutex_unlock(&_mutex);
        return data;
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_full_cond);
        pthread_cond_destroy(&_empty_cond);
    }

private:
    std::queue<T> _q; // 临界资源
    int _cap;
    pthread_mutex_t _mutex;
    pthread_cond_t _full_cond;  // 生产者生产满了，就在这里等待
    pthread_cond_t _empty_cond; // 消费者消费完了，在这里等待

    int _csleep_num;
    int _psleep_num;
};